package release_system

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"github.com/pkg/sftp"
	"golang.org/x/crypto/ssh"
	"io"
	"k8s-platform/config"
	"k8s-platform/dao"
	"k8s-platform/db"
	"k8s-platform/model"
	"os"
	"path/filepath"
	"strings"
	"text/template"
	"time"
)

var K8sDeployHandler k8sDeployHandler

type k8sDeployHandler struct{}

func (*k8sDeployHandler) K8s_install_List() ([]*model.K8sDeployConfig, error) {
	return dao.K8sDeployHandler.K8s_install_List()
}

func (*k8sDeployHandler) Create_Deploy_Config(k8sdeploy *model.K8sDeployConfig) error {
	_, has, err := dao.K8sDeployHandler.Has(k8sdeploy.K8sName, k8sdeploy.ID)
	if err != nil {
		return err // 数据库查询错误（服务器端错误）
	}
	// 修正：如果已存在（has为true），返回重复错误（业务错误）
	if !has {
		return errors.New("该部署集群名称重复已存在，请删除或重新添加")
	}

	if err := dao.K8sDeployHandler.Create_Deploy_Config(k8sdeploy); err != nil {
		return err // 数据库创建错误（服务器端错误）
	}
	return nil
}

func (*k8sDeployHandler) Update_Deploy_Config(config *model.K8sDeployConfig) error {
	_, has, err := dao.K8sDeployHandler.Has(config.K8sName, config.ID)
	if err != nil {
		return err
	}
	if !has {
		return errors.New("该部署集群名称重复已存在，请删除或修改")
	}

	if err := dao.K8sDeployHandler.Update_Deploy_Config(config); err != nil {
		return err
	}
	return nil
}

func (*k8sDeployHandler) Delete_Deploy_Config(Delete int64) error {
	return dao.K8sDeployHandler.Delete_Deploy_Config(Delete)
}

// 远程服务器配置
type RemoteServer struct {
	Address  string // 服务器地址
	User     string // 用户名
	Password string // 密码
}

// 保存配置
func (*k8sDeployHandler) Ansible_init(k8sdeploy *model.K8sDeployConfig) error {
	// 假设要查询的IP是192.168.43.107
	targetIP := k8sdeploy.ClusterDeployIps[0].(string)

	HostServers := &model.HostServer{}

	// 使用MySQL的JSON_CONTAINS函数查询包含目标IP的记录
	// private_ip是JSON数组，需要检查是否包含{"ip": "目标IP"}的元素
	result := db.DB.Model(HostServers).
		Where("JSON_CONTAINS(private_ip, ?, '$')", fmt.Sprintf(`{"ip": "%s"}`, targetIP)).
		Pluck("ssh_port", &HostServers.Ssh_port)

	// 处理查询结果
	if result.Error != nil {
		// 查询失败（如记录不存在）
		return fmt.Errorf("未找到IP为%s的主机: %v", targetIP, result.Error)
	}

	// 查询成功，获取对应的Ssh_port
	sshPort := HostServers.Ssh_port
	if sshPort == "" {
		sshPort = "22" // 若端口为空，使用默认SSH端口
	}

	cred := &model.Credential{}
	credID := k8sdeploy.ClusterDeployCredentials[0]
	// 根据ID查询单条Credential记录
	cluster := db.DB.Where("id = ?", credID).First(&cred)
	if result.Error != nil {
		return fmt.Errorf("查询凭据失败: %v", cluster.Error)
	}

	server := RemoteServer{
		Address:  targetIP + ":" + sshPort,
		User:     cred.User,
		Password: cred.Password,
	}

	// 创建SSH客户端配置
	sshConfig := &ssh.ClientConfig{
		User: server.User,
		Auth: []ssh.AuthMethod{
			ssh.Password(server.Password),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	// 连接到远程服务器
	a := k8sdeploy.ClusterDeployIps[0]
	print(a)
	client, err := ssh.Dial("tcp", server.Address, sshConfig)
	if err != nil {
		return fmt.Errorf("无法连接到服务器: %v", err, k8sdeploy.ClusterDeployIps[0].(string))
	}
	defer client.Close()

	// 创建文件内容模板 - 动态生成master节点信息
	ansible_host := `
[k8s_master]
{{with $masterIps := .ClusterMasterIps}}{{$firstIp := index $masterIps 0}}{{$firstNic := index $.ClusterMasterNics 0}}{{$firstIp}}  {{range $idx, $ip := $masterIps}}master0{{add $idx 1}}="{{$ip}}" {{end}}vip="{{$.PhysicalHaIp}}" pwd="/root/k8s-install" network_dev="{{$firstNic}}"{{end}}

[k8s_master_node]
{{with $masterIps := .ClusterMasterIps}}{{range $nodeIdx, $nodeIp := $masterIps}}{{if ne $nodeIdx 0}}{{$nodeNic := index $.ClusterMasterNics $nodeIdx}}{{$nodeIp}} {{range $idx, $ip := $masterIps}}master0{{add $idx 1}}="{{$ip}}" {{end}}vip="{{$.PhysicalHaIp}}" pwd="/root/k8s-install" network_dev="{{$nodeNic}}"{{end}}
{{end}}{{end}}

[k8s_node]
{{range $index, $ip := .ClusterNodeIps}}{{$ip}} node0{{add $index 1}}="{{$ip}}"  pwd="/root/k8s-install" network_dev="{{index $.ClusterNodeNics $index}}"{{end}}
`

	// 创建自定义函数映射，用于index+1生成master01, master02等
	funcMap := template.FuncMap{
		"add": func(a, b int) int {
			return a + b
		},
	}
	// 解析模板
	tpl, err := template.New("k8sconfig").Funcs(funcMap).Parse(ansible_host)
	if err != nil {
		return fmt.Errorf("模板解析错误: %v", err)
	}

	// 执行模板，生成文件内容
	var contentBuf bytes.Buffer
	if err := tpl.Execute(&contentBuf, k8sdeploy); err != nil {
		return fmt.Errorf("模板执行错误: %v", err)
	}

	// 要在远程服务器上创建的文件路径
	ansible_hostPath := "/tmp/hosts"

	// 创建SSH会话
	session, err := client.NewSession()
	if err != nil {
		return fmt.Errorf("无法创建会话: %v", err)
	}
	defer session.Close()

	// 通过echo命令将内容写入远程文件
	cmd := fmt.Sprintf(" echo %s > %s", escapeShellString(contentBuf.String()), ansible_hostPath)
	if err := session.Run(cmd); err != nil {
		return fmt.Errorf("执行命令错误: 请确认是否安装ansible %v", err)
	}
	return nil
}

// 转义shell字符串中的特殊字符
func escapeShellString(s string) string {
	// 简单的转义实现，根据需要可以增强
	return "'" + strings.ReplaceAll(s, "'", "'\\''") + "'"
}

// 保存配置并执行部署及生成部署文件（核心函数，含日志写入）
func GenerateAutomationScript_Install(k8sdeploy model.K8sDeployConfig) error {
	// 1. 校验核心参数
	if err := validateK8sDeployConfig(k8sdeploy); err != nil {
		return fmt.Errorf("参数校验失败：%w", err)
	}

	// 2. 处理 JSONSlice 类型，转换为字符串切片
	masterIps, err := jsonSliceToStringSlice(k8sdeploy.ClusterMasterIps)
	if err != nil {
		return fmt.Errorf("转换Master IP失败：%w", err)
	}
	masterNics, err := jsonSliceToStringSlice(k8sdeploy.ClusterMasterNics)
	if err != nil {
		return fmt.Errorf("转换Master网卡失败：%w", err)
	}

	// 3. 准备模板数据
	tplData := make(map[string]string)
	tplData["ImagesVersion"] = k8sdeploy.K8sVersion
	tplData["K8sMaster01"] = masterIps[0]
	tplData["K8sVip"] = getDefaultValue(k8sdeploy.PhysicalHaIp, "192.168.43.100")
	tplData["NetworkName"] = masterNics[0]
	tplData["FrpName"] = getDefaultValue(k8sdeploy.K8sName, "k8s-cluster-frp")
	tplData["FrpPort"] = "30015"
	tplData["ImageRepo"] = k8sdeploy.ImageRepo
	tplData["K8sClusterName"] = k8sdeploy.K8sName
	tplData["ContainerRuntime"] = k8sdeploy.ContainerRuntime
	tplData["NetworkComponent"] = k8sdeploy.NetworkComponent
	tplData["InstallUser"] = "{{ansible_user_id}}"

	// 处理clusterMasterNics赋值逻辑
	tplData["clusterMasterNics01"] = masterNics[0]
	if len(masterNics) >= 2 {
		tplData["clusterMasterNics02"] = masterNics[1]
	} else {
		tplData["clusterMasterNics02"] = masterNics[0]
	}
	if len(masterNics) >= 3 {
		tplData["clusterMasterNics03"] = masterNics[2]
	} else {
		tplData["clusterMasterNics03"] = masterNics[0]
	}

	// 动态添加Master02/03（仅3主架构）
	if len(masterIps) == 3 {
		tplData["K8sMaster02"] = masterIps[1]
		tplData["K8sMaster03"] = masterIps[2]
	}

	// 4. 检查模板文件是否存在
	if _, err := os.Stat(config.AutomationTplPath); os.IsNotExist(err) {
		return fmt.Errorf("模板文件不存在：%s", config.AutomationTplPath)
	}

	// 5. 解析模板
	tpl, err := template.ParseFiles(config.AutomationTplPath)
	if err != nil {
		return fmt.Errorf("解析模板失败：%w", err)
	}

	// 6. 创建输出脚本文件
	outputFile, err := os.Create(config.AutomationScriptPath)
	if err != nil {
		return fmt.Errorf("创建脚本文件失败：%w", err)
	}
	defer outputFile.Close()

	// 7. 渲染模板并写入数据
	if err := tpl.Execute(outputFile, tplData); err != nil {
		return fmt.Errorf("模板渲染失败：%w", err)
	}

	// 8. 设置脚本可执行权限（确保本地权限正确）
	if err := os.Chmod(config.AutomationScriptPath, 0755); err != nil {
		return fmt.Errorf("设置脚本权限失败：%w", err)
	}

	// ---------------------- SSH上传与执行逻辑 ----------------------
	// 获取目标IP
	targetIP, ok := k8sdeploy.ClusterDeployIps[0].(string)
	if !ok {
		return errors.New("ClusterDeployIps第一个元素非字符串类型")
	}

	// 查询目标主机SSH端口
	hostServer := &model.HostServer{}
	result := db.DB.Model(hostServer).
		Where("JSON_CONTAINS(private_ip, ?, '$')", fmt.Sprintf(`{"ip": "%s"}`, targetIP)).
		Pluck("ssh_port", &hostServer.Ssh_port)
	if result.Error != nil {
		return fmt.Errorf("未找到IP为%s的主机: %v", targetIP, result.Error)
	}
	sshPort := hostServer.Ssh_port
	if sshPort == "" {
		sshPort = "22"
	}

	// 查询凭据（用户名/密码）
	cred := &model.Credential{}
	credID := k8sdeploy.ClusterDeployCredentials[0]
	if err := db.DB.Where("id = ?", credID).First(cred).Error; err != nil {
		return fmt.Errorf("查询凭据失败: %v", err)
	}

	// 构建SSH客户端配置（增加超时时间）
	sshConfig := &ssh.ClientConfig{
		User:            cred.User,
		Auth:            []ssh.AuthMethod{ssh.Password(cred.Password)},
		Timeout:         30 * time.Second,
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	// 连接远程服务器
	client, err := ssh.Dial("tcp", targetIP+":"+sshPort, sshConfig)
	if err != nil {
		return fmt.Errorf("无法连接到服务器%s:%s: %w", targetIP, sshPort, err)
	}
	defer client.Close()

	// 创建SFTP客户端上传脚本
	sftpClient, err := sftp.NewClient(client)
	if err != nil {
		return fmt.Errorf("创建SFTP客户端失败: %w", err)
	}
	defer sftpClient.Close()

	// 本地脚本路径 → 远程脚本路径
	localScriptPath := config.AutomationScriptPath
	remoteScriptName := filepath.Base(localScriptPath)
	remoteScriptPath := fmt.Sprintf("/home/%s/%s", cred.User, remoteScriptName)

	// 上传前检查远程目录权限
	sessionCheck, err := client.NewSession()
	if err != nil {
		return fmt.Errorf("创建权限检查会话失败: %w", err)
	}
	checkCmd := fmt.Sprintf("mkdir -p /home/%s && chmod 755 /home/%s", cred.User, cred.User)
	if err := sessionCheck.Run(checkCmd); err != nil {
		sessionCheck.Close()
		return fmt.Errorf("远程目录权限配置失败: %w", err)
	}
	sessionCheck.Close()

	// 打开本地文件并上传
	localFile, err := os.Open(localScriptPath)
	if err != nil {
		return fmt.Errorf("打开本地脚本失败: %w", err)
	}
	defer localFile.Close()

	remoteFile, err := sftpClient.Create(remoteScriptPath)
	if err != nil {
		return fmt.Errorf("创建远程文件%s失败: %w", remoteScriptPath, err)
	}
	defer remoteFile.Close()

	// 上传文件并确保远程权限
	if _, err := io.Copy(remoteFile, localFile); err != nil {
		return fmt.Errorf("上传脚本到%s失败: %w", remoteScriptPath, err)
	}
	if err := sftpClient.Chmod(remoteScriptPath, 0755); err != nil {
		return fmt.Errorf("设置远程脚本权限失败: %w", err)
	}
	fmt.Printf("脚本已上传至: %s:%s\n", targetIP, remoteScriptPath)

	// ---------------------- 执行远程脚本（含日志写入逻辑）----------------------
	session, err := client.NewSession()
	if err != nil {
		return fmt.Errorf("无法创建执行会话: %w", err)
	}
	defer session.Close()

	// 【新增】初始化本地日志文件（以K8sName为前缀）
	logDir := config.K8s_install_logs // 需在config中定义：LogDir = "./logs"
	if err := os.MkdirAll(logDir, 0755); err != nil {
		return fmt.Errorf("创建日志目录失败：%w", err)
	}
	// 日志文件名：K8sName + 时间戳（避免重复）
	logFileName := fmt.Sprintf("%s_%s.log", k8sdeploy.K8sName, time.Now().Format("20060102150405"))
	logFilePath := filepath.Join(logDir, logFileName)
	logFile, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		return fmt.Errorf("创建日志文件失败：%w", err)
	}
	defer logFile.Close()
	logWriter := bufio.NewWriter(logFile) // 带缓冲写入

	// 【新增】创建管道合并stdout和stderr，实时写入日志
	reader, writer := io.Pipe()
	session.Stdout = writer // 标准输出定向到管道写入端
	session.Stderr = writer // 错误输出也定向到管道写入端

	// 【新增】启动goroutine实时读取管道并写入日志
	go func() {
		defer writer.Close() // 确保写入端最终关闭
		scanner := bufio.NewScanner(reader)
		timestampFormat := "2006-01-02 15:04:05" // 日志时间格式
		for scanner.Scan() {
			line := scanner.Text() // 读取一行输出
			// 格式化日志：[时间] 内容
			logLine := fmt.Sprintf("[%s] %s\n", time.Now().Format(timestampFormat), line)
			// 写入本地日志文件
			if _, err := logWriter.WriteString(logLine); err != nil {
				fmt.Printf("日志写入失败：%v（内容：%s）\n", err, line)
			}
			logWriter.Flush() // 实时刷新，确保日志落盘
		}
		if err := scanner.Err(); err != nil {
			fmt.Printf("日志扫描错误：%v\n", err)
		}
	}()

	// 执行命令
	cmd := fmt.Sprintf("/bin/bash -l %s ", remoteScriptPath)
	fmt.Printf("执行远程命令: %s\n", cmd)

	// 执行并设置超时
	errChan := make(chan error, 1)
	// 【新增】定义时间格式变量（与日志写入的格式保持一致）
	timestampFormat := "2006-01-02 15:04:05"

	go func() {
		errChan <- session.Run(cmd)
	}()
	select {
	case err := <-errChan:
		if err != nil {
			// 错误信息写入日志
			errLog := fmt.Sprintf("[%s] 脚本执行失败: %v\n", time.Now().Format(timestampFormat), err)
			logWriter.WriteString(errLog)
			logWriter.Flush()
			return fmt.Errorf(
				"脚本执行失败: %w\n详情见日志: %s",
				err,
				logFilePath,
			)
		}
		// 成功信息写入日志
		successLog := fmt.Sprintf("[%s] 脚本执行成功\n", time.Now().Format(timestampFormat))
		logWriter.WriteString(successLog)
		logWriter.Flush()
	case <-time.After(15 * time.Minute):
		// 超时信息写入日志
		timeoutLog := fmt.Sprintf("[%s] 脚本执行超时（15分钟）\n", time.Now().Format(timestampFormat))
		logWriter.WriteString(timeoutLog)
		logWriter.Flush()
		return fmt.Errorf("脚本执行超时（15分钟）\n详情见日志: %s", logFilePath)
	}

	// 打印成功信息
	fmt.Printf("脚本执行成功，日志已保存至: %s\n", logFilePath)
	return nil
}

// 辅助函数
func validateK8sDeployConfig(req model.K8sDeployConfig) error {
	requiredFields := map[string]string{
		"K8sName":          req.K8sName,
		"OsVersion":        req.OsVersion,
		"K8sVersion":       req.K8sVersion,
		"ImageRepo":        req.ImageRepo,
		"NetworkComponent": req.NetworkComponent,
		"ContainerRuntime": req.ContainerRuntime,
	}
	for fieldName, fieldVal := range requiredFields {
		if fieldVal == "" {
			return fmt.Errorf("%s不能为空", fieldName)
		}
	}

	masterIpLen := len(req.ClusterMasterIps)
	if masterIpLen != 1 && masterIpLen != 3 {
		return fmt.Errorf("Master节点IP需传入1个或3个，当前%d个", masterIpLen)
	}

	if len(req.ClusterMasterNics) < 1 {
		return errors.New("Master节点网卡不能为空")
	}

	if len(req.ClusterDeployIps) < 1 {
		return errors.New("部署IP列表不能为空")
	}
	if len(req.ClusterDeployCredentials) < 1 {
		return errors.New("部署凭据列表不能为空")
	}

	return nil
}

func jsonSliceToStringSlice(jsonSlice model.JSONSlice) ([]string, error) {
	var strSlice []string
	for _, item := range jsonSlice {
		strItem, ok := item.(string)
		if !ok {
			return nil, errors.New("JSONSlice中存在非字符串元素")
		}
		strSlice = append(strSlice, strItem)
	}
	return strSlice, nil
}

func getDefaultValue(field, defaultValue string) string {
	if field == "" {
		return defaultValue
	}
	return field
}
